﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace JOATServer.SocietyFramework.Core
{
    /// <summary>
    /// List used to handle messages. It simulates a queue where adding is done only by one thread at a time.
    /// Removing from the list can block if there is no Message yet.
    /// Methods are synchronized and removing from the list can block execution until there is something to remove or the timer ends.
    /// Author: Micu Andrei
    /// </summary>
    [Serializable]
    public class MessageList
    {
        protected object removeLock = new object();

        [Serializable]
        protected class RemoveLock {}

        [Serializable]
        protected class Node
        {
            public Message Message { get; set; }
            public Node Next { get; set; }
            public Node(Message message, Node next)
            {
                this.Message = message;
                this.Next = next;
            }
        }

        protected Node first = null;
        protected Node last = null;

        /// <summary>
        /// Adds a message at the end of the queue.
        /// </summary>
        /// <param name="msg">the Message to add</param>
        public void Add(Message msg)
        {
            lock (this)
            {
                if (first == null)
                {
                    first = last = new Node(msg, null);		    //verifies the first element which is sensitive
                }
                else
                {
                    last.Next = new Node(msg, null);
                    last = last.Next;
                }
                lock (removeLock)
                {
                    Monitor.Pulse(removeLock);					//notifies one of the consumer threads that an element has been added
                }
            }
        }

        /// <summary>
        /// Method that returns the element from the head of the queue. It blocks if the list is empty until there is something to return.
        /// </summary>
        /// <returns>the Message at the head of the queue</returns>
        public Message Remove()
        {
            lock (removeLock)
            {
                if (first == null)
                {
                    Monitor.Wait(removeLock);
                    if (first == null) return null;
                }
                Node q = first;
                first = first.Next;
                return q.Message;
            }
        }

        
        /// <param name="timer">the amount of milliseconds to wait until the function returns</param>
        /// <returns>the Message at the head of the queue or null if the timeout expired</returns>
        public Message Remove(int timer)
        {
            lock (removeLock)
            {
                if (first == null)
                {
                    Monitor.Wait(removeLock, timer);
                    if (first == null) return null;
                }
                Node q = first;
                first = first.Next;
                return q.Message;
            }
        }


        /// <summary>
        /// Notifies the thread that waits to remove an element from the list to unblock it.
        /// </summary>
        public void CancelRemoving()
        {
            lock (removeLock)
            {
                Monitor.Pulse(removeLock);
		    }
	    }

    }
}
